/*
 * Implementation of the The Sudoku puzzle module.
 *
 * In keeping with standard Sudoku nomenclature, we use
 * 1-based indexing. Thus row indices, column indices,
 * and digits being placed must all be in the range 1 .. 9.
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "puzzle.h"
#include "bool.h"

/*
 * The tables are 10x10 so that we can index from 1 .. 9 (the 0th row
 * and column are ignored). This wastes a tad of space, but is much
 * less likely to cause "off by 1" indexing errors.
 *
 * puzzle[i][j] is the number (0 .. 9) at row i, column j.
 *   0 represents a free (blank) puzzle location.
 *   Anything else represents the value at that location, and
 *     must be consistent with the rules of Sudoku.
 *
 * fixed[i][j] is true <=> row i, column j is fixed.
 *   That is, the value was part of the initial puzzle layout and
 *   cannot be erased.
 */

static short puzzle[10][10] ;
static bool  fixed[10][10] ;

/*
 * Functions to determine whether a row, a column, or the "region"
 * containing a row and column, contains the specified digit.
 */
static bool row_contains(int row, int digit) ;
static bool col_contains(int col, int digit) ;
static bool region_contains(int row, int col, int digit) ;

/*
 * Function to determine whether an integer - not the character for
 * a digit - is in the range 0 .. 9.
 */
static bool in_range(int value) ;

/*
 * Print support functions.
 *   print a line of dashes.
 *   print a row of values (with blanks for 0's).
 */
static void print_dashes() ;
static void print_row(int row) ;

/*
 * Initialize the puzzle so that (a) all values are zero (free)
 * and (b) non of the values are 'fixed' (uneraseable).
 */

extern void init_puzzle()  {
	int i, j;
	for(i = 0; i < 10; i++){
		for(j = 0; j < 10; j++){
			puzzle[i][j] = 0;
		}
	}
}

/*
 * Read in the initial puzzle configuration.
 * Each line is 4 characters long:
 *   Row    as a character '0' .. '9'
 *   Column as character '0' .. '9'
 *   Digit  as character '0' .. '9'
 *   Terminating newline.
 * Exits with an error message if there are syntactic
 * or semantic errors with any configuration line.
 */

void configure(FILE *puzzle_file) {
	int line_num = 0;
	char config[BUFSIZ]; /*used to read configuration file*/

	/*reads configuration file line by line and for each line,
	 *checks the first 3 characters for valid format and placement.
	 *If configuration numbers are valid, they are placed and fixed on the puzzle*/
	while((fgets(config, BUFSIZ, puzzle_file)) != NULL){
		line_num += 1;

		int i;
		for(i = 0; i < 3; i++){
			if(!in_range((config[i] - '0'))){
				printf("Illegal format in configuration file at line %d\n", line_num);
				exit(1);
			}
		}

		int row = config[0] - '0';
		int column = config[1] - '0';
		short digit = config[2] - '0';

		memset(config, 0, sizeof(config));	/*clear config array*/

		if(!fixed[row][column] && !row_contains(row, digit) && !col_contains(column, digit)
				&& !region_contains(row, column, digit)){
			puzzle[row][column] = digit;
			fixed[row][column] = TRUE;
		}
		else
		{
			printf("Illegal placement in configuration file at line %d\n", line_num);
			exit(1);
		}
	}
}

/*
 * Print the puzzle to standard output using the
 * specified output format.
 * 	- Print an initial row of dashes.
 * 	- Print each row.
 * 	- Print a row of dashes after each 3rd row.
 */

void print_puzzle() {
	print_dashes();
	int i;
	for(i = 1; i < 10; i ++){
		print_row(i);
		if(i % 3 == 0){
			print_dashes();
		}
	}
}

/*
 * (Attempt to) add the specified digit at the given row and
 * column location on the puzzle.
 * The digit, row, and column must all be integers in
 * the range 1 .. 9.
 *
 * Return value is the status of the attempt (OP_OK is the only
 * status where the puzzle is actually changed).
 */

op_result add_digit(int row, int col, int digit) {
	if(!in_range(row)||!in_range(col)||!in_range(digit)){
		return OP_BADARGS;
	}
	else if(puzzle[row][col] != 0){
		return OP_OCCUPIED;
	}
	else if(row_contains(row, digit) || col_contains(col, digit)
			|| region_contains(row, col, digit)){
		return OP_ILLEGAL;
	}
	else{
		puzzle[row][col] = digit;
		return OP_OK ;
	}
}

/*
 * (Attempt to) delete the digit at the given row and
 * column location on the puzzle the row and column
 * must both be integers in the range 1 .. 9
 *
 * Return value is the status of the attempt (OP_OK is the only
 * status where the puzzle actually changed).
 */

op_result erase_digit(int row, int col) {
	if(!in_range(row)||!in_range(col)){
		return OP_BADARGS;
	}
	else if(puzzle[row][col] == 0){
		return OP_OCCUPIED;
	}
	else if(fixed[row][col]){
		return OP_FIXED;
	}
	else{
		puzzle[row][col] = 0;
		return OP_OK ;
	}
}

/*
 * Returns TRUE iff the given 'row' has the given 'digit' in it.
 */
static bool row_contains(int row, int digit) {
	int i;
	for(i = 1; i < 10; i++){
		if(puzzle[row][i] == digit){
			return TRUE;
		}
	}
	return FALSE ;
}

/*
 * Returns TRUE iff the given 'col' has the given 'digit' in it.
 */
static bool col_contains(int col, int digit) {
	int i;
	for(i = 1; i < 10; i++){
		if(puzzle[i][col] == digit){
			return TRUE;
		}
	}
	return FALSE ;
}

/*
 * Returns TRUE iff the region containing the cell at 'row' & 'col'
 * has the given digit in it.
 * NOTE: Finding the first row and column for the 'row'
 *       and 'col' is simple if you (a) remember that integer
 *       division discards the remainder and (b) translating
 *       the indices to 0 rather than 1 base makes things easier.
 * NOTE: A region is a 3 x 3 square.
 */
static bool region_contains(int row, int col, int digit) {

	/*calculate start and end of row and column*/
	int row_s = ((row - 1) / 3) * 3 + 1;
	int row_e = row_s + 2;
	int col_s = ((col - 1) / 3) * 3 + 1;
	int col_e = col_s + 2;

	int i, j;
	for(i = row_s; i <= row_e; i++){
		for(j = col_s; j <= col_e; j++){
			if(puzzle[i][j] == digit){
				return TRUE;
			}
		}
	}

	return FALSE ;
}

/*
 * Return TRUE iff the value is in the Sudoku range (1 .. 9)
 */
static bool in_range(int value) {
	if(value >= 1 && value <= 9){
		return TRUE;
	}
	else{
		return FALSE;
	}

}

/*
 * Print a row of 25 dashes.
 * This will line up correctly with the rows with data.
 */
static void print_dashes() {
	printf("-------------------------\n");
}

/*
 * Print a row from the puzzle.
 *   Print an initial bar ('|').
 *   For each value in the row, print a space and either:
 *     - a space if the value is 0.
 *     - the CHARACTER code for the digit if non-zero.
 *   After the 3rd, 6th and 9th columns, print " |"
 */
static void print_row(int row) {
	printf("|");
	int i;
	for(i = 1; i < 10; i++){
		if(puzzle[row][i] != 0){
			short value = puzzle[row][i];
			printf(" %d", value);
		}
		else{
			printf("  ");
		}
		if(i % 3 == 0){
			printf(" |");
			if(i == 9){
				printf("\n");
			}
		}
	}
}
